
#ifndef _TTG_MATH_GEOMETRY_
#define _TTG_MATH_GEOMETRY_
#include "Common/Definitions.h"

#include "vec3x.h"

namespace ttg
{
	namespace math
	{
		//Represents triangle (as three point) and contains useful routines.
		class Triangle
		{
			private:
				vec3f points[3];
			public:
				//Creates empty triangle. Use set() to define it.
				inline Triangle()
						{ points[2] = points[1] = points[0] = vec3f(); }
				//Creates triangle with given points.
				inline Triangle(const vec3f &a, const vec3f &b, const vec3f &c)
						{ points[0] = a; points[1] = b; points[2] = c; }
				//Sets triangle points.
				inline void set(const vec3f &a, const vec3f &b, const vec3f &c)
						{ points[0] = a; points[1] = b; points[2] = c; }
				//Returns n-th point.
				inline vec3f &operator [](const int n)
						{ return points[n]; }
				inline const vec3f &operator [](const int n) const
						{ return points[n]; }
				//Returns first point.
				inline vec3f &p1()
						{ return points[0]; }
				inline const vec3f &p1() const
						{ return points[0]; }
				//Returns second point.
				inline vec3f &p2()
						{ return points[1]; }
				inline const vec3f &p2() const
						{ return points[1]; }
				//Returns third point.
				inline vec3f &p3()
						{ return points[2]; }
				inline const vec3f &p3() const
						{ return points[2]; }
				//Returns true, if given point is inner.
				//Note: point must belong to triangle plane.
				bool isInnerPoint(const vec3f &point);
				//Checks given triangles for intersection.
				//If they do, assigns ipoint to point of intersection.
				inline bool isIntersected(const Triangle &t, vec3f &ipoint)
						{ return Triangle::areIntersected(*this, t, ipoint); }
				static bool areIntersected(const Triangle &t1, const Triangle &t2, vec3f &ipoint);
		};

		class Line;

		class Plane
		{
			private:
				float coeff[4]; //representation as Ax + By + Cz + D = 0
				int nonzero; //index of nonzero coefficient. -1 for uncorrect plane.
			public:
				//Implicit definition.
				Plane(float A, float B, float C, float D);
				//Definition as three points.
				Plane(vec3f a, vec3f b, const vec3f &c);
				//Definition as point + normal.
				Plane(const vec3f &point, const vec3f &normal);
				//Definition by TTGTriangle.
				Plane(const Triangle &t);
				//Returns n-th coefficient.
				inline float operator[](const int n) const
						{ return coeff[n]; }
				inline float A() const
						{ return coeff[0]; }
				inline float B() const
						{ return coeff[1]; }
				inline float C() const
						{ return coeff[2]; }
				inline float D() const
						{ return coeff[3]; }
				const Plane operator -(const Plane &p) const;
				const Plane operator +(const Plane &p) const;
				const Plane &operator =(const Plane &p);
				const Plane operator *(float t) const;
				//Returns normal.
				//Note: it's not normalized.
				inline vec3f getNormal() const
						{ return vec3f(coeff[0], coeff[1], coeff[2]); }
				//Returns true, if plane is correct.
				inline bool isCorrect() const
						{ return nonzero >= 0; }
				//Returns index of nonzero coeffecient or -1 in case of nonexistence.
				inline int getIndexOfNonZeroCoeff() const
						{ return nonzero; }
				//Returns value of the plane equation at given point.
				inline float getValueAt(const vec3f &point) const
						{ return A() * point.x + B() * point.y + C() * point.z + D(); }
				//Returns true, if line intersects current plane.
				inline bool isIntersected(const Line &l, float &t) const
						{ return Plane::areIntersected(*this, l, t); }
				inline bool isIntersected(const Line &l, vec3f &ipoint) const
						{ return Plane::areIntersected(*this, l, ipoint); }
				//Returns true, if line intersects plane.
				static bool areIntersected(const Plane &p, const Line &l, float &t);
				static bool areIntersected(const Plane &p, const Line &l, vec3f &ipoint);
		};

		class Line
		{
			private:
				bool correct;
				vec3f _P; //f(t) = P*t+Q;
				vec3f _Q;
			public:
				//Definition as f(t) = P * t + Q;
				Line(const vec3f &P, const vec3f &Q);
				//Definition as intersection of two planes.
				Line(const Plane &m, const Plane &n);
				//Returns normal, which belongs to given plane.
				inline vec3f getNormal(const Plane &p) const
						{ return crossProduct(p.getNormal(), _P); }
				//Return true, if this line is real line (P != 0.0f).
				inline bool isCorrect() const
						{ return correct; }
				//Returns direction vector.
				inline const vec3f &P() const
						{ return _P; }
				//Returns start vector.
				inline const vec3f &Q() const
						{ return _Q; }
				//Returns P * t + Q.
				inline vec3f getValueAt(float t) const
						{ return _P * t + _Q; }
				//Returns true, if given line intersects current.
				//ipoint will be contain point of intersection.
				bool isIntersected(const Line &l, vec3f &ipoint) const
						{ return Line::areIntersected(*this, l, ipoint); }
				static bool areIntersected(const Line &l1, const Line &l2, vec3f &ipoint);
		};
	}
}

#endif
